package game;

import java.util.*;

public class BoardState {
	int[] cardsUsed = new int[80];
	int[] rack;
	int[] weights;
	int[] knownOppCards;
	TreeSet<Integer> unavailableNumbers;
	int knownOppMoves, prevDiscard;
	double oppStyle;

	public enum Move {
		REQUEST_DISCARD, REQUEST_DECK
	}

	public enum OpponentMove {
		TAKE_DISCARD, TAKE_DECK, NO_MOVE, ILLEGAL, TIMED_OUT
	}

	public int numToReplace;

	public BoardState() {
		rack = new int[20];
		Random rand = new Random();
		for (int i = 0; i < rack.length; i++) {
			rack[i] = rand.nextInt(80) + 1;
			cardsUsed[rack[i] - 1] = 1;
		}
	}

	/**
	 * Restars the board to a new, unplayed state.
	 * 
	 * @param gameID
	 *            An integer representing the game you are currently playing.
	 *            This is purely advisory, but may be useful in your debugging
	 *            and logging. This integer may also be used to lookup the game
	 *            on the website.
	 * @param playerID
	 *            A zero-based index indicating which player you are this round.
	 *            Player 0 goes first, player 1 goes second.
	 * @param initialDiscard
	 *            A number indicating what the initial discard is.
	 * @param otherPlayerID
	 *            The team ID of the other player, which can be used to go to
	 *            the team page by feeding it in the obvious manner to the
	 *            /team/ URI.
	 */
	public void resetBoard(int gameID, int playerID, int initialDiscard,
			int otherPlayerID) {
		rack = new int[20];
	}

	/**
	 * Determine which move to make, given the current state and the opponent's
	 * previous move.
	 * 
	 * @param gameID
	 *            An integer matching the one sent with the corresponding
	 *            start_game call.
	 * @param rack
	 *            This is an array of ints, corresponding to the current 20
	 *            cards in your rack.
	 * @param discard
	 *            This is a single int, corresponding to the top card on the
	 *            discard pile.
	 * @param remainingMicro
	 *            An integer which is the approximate count of the remaining
	 *            microseconds available to your bot in this game.
	 * @param oppMove
	 *            The opponent's move type
	 * @param idx
	 *            The position in the opponents rack which was replaced. Will
	 *            only be valid if oppMove is TAKE_DISCARD, and will be -1
	 *            otherwise
	 * @return The move you decide to make. NOTE - at the end you must ALSO set
	 *         the numToReplace variable to the position in your hand you wish
	 *         to replace, 0 indexed
	 */
	public Move getMove(int gameID, int[] rack, int discard,
			int remainingMicro, OpponentMove oppMove, int idx) {
		this.rack = rack;
		cardsUsed[discard - 1] = 1;
		numToReplace = determineCardPosition(discard);
		return Move.REQUEST_DECK;
	}

	/* uses a few methods of determining in what order to evict stuff */
	public int[] rackAcceptable() {

		weights = new int[20];

		for (int i = 0; i < rack.length; i++) {
			weights[i] -= (int) Math.abs(i - rack[i] / 4);
		}

		/* first pass run weights solely on position */
		/*
		 * for(int i=0; i < rack.length; i++) { if(inArea(rack[i], i,0.0)) {
		 * ////System.out.printf("%d is in range!\n", rack[i]); weights[i]+=15;
		 * } else if(inArea(rack[i], i, 0.35)) { weights[i]+=8; } else
		 * if(inArea(rack[i], i, 0.55)) { weights[i]+=3; }
		 * 
		 * if(outArea(rack[i], i, 4)) {
		 * ////System.out.printf("%d is in a bad place!\n", rack[i]); weights[i]
		 * +=(weights[i] == 0) ? 0: -1; } if(outArea(rack[i], i, 10)) {
		 * ////System.out.printf("%d is in a VERY bad place!\n", rack[i]);
		 * weights[i]--;; } }
		 */
		/* consectives */
		for (int i = 1; i < rack.length; i++) {
			if (rack[i - 1] < rack[i])
				weights[i - 1] += 2;
		}

		/* check for impossible values */
		for (int i = 0; i < rack.length; i++) {
			if (rack[i] <= i || (rack.length) - i - 1 > (80 - rack[i])) {
				// //System.out.printf("%d is in an impossible spot\n",
				// rack[i]);
				weights[i] += -3;
			}
		}
		// //System.out.println("Weights: "+Arrays.toString(weights));
		// //System.out.println("--");

		return weights;
	}

	/* checks range within 20% or so percent */
	public boolean inArea(int val, int i, double pct) {
		return val <= ((i + 1) * 80.0 / (rack.length)) + (pct) * rack.length
				&& val + (pct) * rack.length > (((i + 1) * 80.0 / (rack.length)) - (80.0 / (rack.length)));
	}

	public boolean outArea(int val, int i, int spaces) {
		return Math.abs(((Math.ceil(val / (80.0 / (rack.length)))) - i)) > spaces;
	}

	public int getPlaceToMove(int[] rack, int cardNum) {
		int locToMove = 0;
		return locToMove;
	}

	/**
	 * Called when you decide to request a card from the deck. Gives you a card
	 * from the deck and your current rack. At the end, set the numToReplace
	 * variable to what you want to replace
	 * 
	 * @param gameID
	 *            An integer matching the one sent with the corresponding
	 *            start_game call.
	 * @param remainingMicro
	 *            An integer which is the approximate count of the remaining
	 *            microseconds your bot has for this game.
	 * @param rack
	 *            An array of 20 integers corresponding to your rack, for your
	 *            convenience. (There will have been no changes since the
	 *            get_move call.)
	 * @param card
	 *            An integer representing the card number that you must place in
	 *            your rack.
	 */
	public void getDeckExchange(int gameID, int remainingMicro, int[] rack,
			int card) {
		cardsUsed[card - 1] = 1;
		this.rack = rack;
		numToReplace = determineCardPosition(card);
	}

	/**
	 * Determines where in your rack it is best to place a given card, replacing
	 * the card already there. HEURISTICY STUFF GOES HERE!!!
	 * 
	 * @param card
	 *            The card you are trying to insert
	 * @return The best position to put the card
	 */
	public int determineCardPosition(int card) {
		ArrayList<Integer> evictList = new ArrayList<Integer>();
		int[] badStuff = rackAcceptable();
		int min = badStuff[0];
		for (int i : badStuff) {
			if (i < min)
				min = i;
		}
		for (int i = 0; i < badStuff.length; i++) {
			if (badStuff[i] == min)
				evictList.add(i);
		}

		/*
		 * int maxInc = Integer.MIN_VALUE; int insertionPosition = 0; for(int i
		 * = 0; i < evictList.size(); i++){ rackAcceptable(); int temp =
		 * rack[i]; int startWeight = weights[i]; rack[i] = card;
		 * rackAcceptable(); int diff = weights[i] - startWeight; if(diff >
		 * maxInc){ maxInc = diff; insertionPosition = i; } rack[i] = temp; }
		 */

		for (int i : evictList) {
			if (inArea(card, i, 0.0))
				return i;
		}

		for (int i : evictList) {
			if (inArea(card, i, 0.35))
				return i;
		}

		for (int i : evictList) {
			if (inArea(card, i, 0.55))
				return i;
		}

		// return insertionPosition;
		return evictList.get((int) (Math.random() * evictList.size()));
	}

	public int cardsPossibleBetweenNums(int myMin, int myMax) {

		int cardsBetween = myMax - myMin - 1;
		int usedBetween = 0;
		for (int i = myMin; i < myMax - 1; i++) {
			if (cardsUsed[i] == 1)
				usedBetween++;
		}
		cardsBetween -= usedBetween;
		return cardsBetween;
	}

}
